<?php
Class QuestionModel extends Model
{
	public $id;
	public $description;
	public $answers;
	public $options;
	public $parameters;
	public $contents;
	public $hasoptions;
	public $categoryid;
	public $taskid;
	
	public function __construct()
	{
		parent::Model();
		$this->hasoptions=false;
		$this->id=-1;
	}
	
	public function DeleteByID($id)
	{
		$this->db->trans_start();
		
		try {
			$sql="delete from questionpool where QuestionID=$id";
			$this->db->qeury($sql);
			$sql="delete From questioncontentinfo where QuestionID=$id";
			$this->db->query($sql);
			$sql="delete From optionbasicinfo where QuestionID=$id";
			$this->db->query($sql);
			$sql="delete From answerbasicinfo where QuestionID=$id";
			$this->db->query($sql);
			$sql="select ID From parameterbasicinfo where QuestionID=$id";
			$query=$this->db->query($sql);
			foreach ($query->result() as $pbrow)
			{
				$pbi=$pbrow->ID;
				$subsql="Delete from parametervalueinfo where ParameterID=$pbi";
				$this->db->query($subsql);
			}
			$sql="delete from paramterbasicinfo where QuestionID=$id";
			$this->db->qeury($sql);
			$sql="delete from questionbasicinfo where ID=$id";
			$this->db-query($sql);
		} catch (Exception $e) 
		{
			$this->db->trans_complete();
			return 0;
		}
		
		$this->db->trans_complete();
		return 1;
	}
	
	public function Get()
	{
		//by id
		//read id to $id
		
		try {
			//select parameters
			$sql="select ID,CategoryID,TaskID,HasOption,CreationDate From questionbasicinfo where ID=$id";
			$query=$this->db->query($sql);
			
			$question=$query->row();
			$this->id=$question->ID;
			$this->categoryid=$question->CategoryID;
			$this->taskid=$question->TaskID;
			$this->hasoptions=$question->HasOption;

			//select parameters
			$sql="select ID,Name From parameterbasicinfo where QuestionID=$id";
			$query=$this->db->query($sql);
			foreach ($query->result as $row)
			{
				$this->parameters[$row->ID]=new Parameter();
				$param=&$this->parameters[$row->Name];
				$param->ID=$row->ID;
				$param->Name=$row->Name;
				
				$pid=$row->ID;
				$subsql="select ID,Value,Type,`From`,`To` from parametervalueinfo where ParameterID=$pid";
				$subquery=$this->db->query($subsql);
				foreach ($subquery->result as $srow)
				{
					$param->Values[$srow->ID]=new ParameterValues();
					$val=&$param->Values[$srow->ID];
					$val->ID=$srow->ID;
					$val->Content=$srow->Value;
					$val->Type=$srow->Type;
					$val->From=$srow->From;
					$val->To=$srow->To;
				}
			}
			
			//select answers
			$sql="select ID,Content,QuestionID,Difficluty,Parameters from answerbasicinfo where QuestionID=$id Order By ID";
			$query=$this->db->query($sql);
			foreach ($query->result() as $row)
			{
				$this->answers[$row->ID]=new Answer();
				$ans=&$this->answers[$row->ID];
				$ans->ID=$row->ID;
				$ans->Content=$row->Content;
				$ans->Difficulty=$row->Difficulty;
				$ans->FillParameters($row->Parameters,$this->parameters);
			}
			
			//select contents
			$sql="select ID,Content,AnswerID,Parameters from questioncontentinfo where QuestionID=$id Order By' ID";
			$query=$this->db->query($sql);
			foreach ($query->result() as $row)
			{
				$this->contents[$row->ID]=new QuestionContent();
				$con=&$this->contents[$row->ID];
				$con->ID=$row->ID;
				$con->Content=$row->Content;
				$con->FillAnswer($row->AnswerID,$this->answers);
				$con->FillParameters($row->Parameters,$this->parameters);
			}
			
			//select options
			$sql="select ID,Content,RelativeQuestion,Parameters,Index from optionsbasicinfo where QuestionID=$id";
			$query=$this->db->query($sql);
			foreach ($query->result() as $row)
			{
				$this->options[$row->ID]=new Option();
				$opt=&$this->options[$row->ID];
				$opt->ID=$row->ID;
				$opt->Content=$row->Content;
				$opt->Index=$row->Index;
				$opt->FillRelatives($row->RelativesQuestion,$this->contents);
				$opt->FillParameters($row->Parameters,$this->parameters);
			}
		} catch (Exception $e) {
			return 0;
		}
		
		//output
			
		return 1;
	}
	
	private function Read($IsNew)
	{
		//read or check
		//input basic info
	}
	
	public function Add($IsModified=false)
	{
		$this->db->trans_start();
		$this->Read(true);
		if ($this->id<0) return 0;
			
		foreach ($this->parameters as $param)
		{
			if ($param->IsNew)
			{
				$data=$param->GetData($this->id);
				try 
				{
					$this->db->insert('parameterbasicinfo',$data);
					$param->ID=$this->db->insert_id();
				} catch (Exception $e) 
				{
					$this->db->trans_complete();
					return 0;
				}				
			}
			
			foreach ($param->Values as $val)
			{
				if (!$val->IsNew) continue;
				$data=$val->GetData($param->ID);
				try 
				{
					$this->db->insert('parametervalueinfo',$data);
					$val->ID=$this->db->insert_id();
				} catch (Exception $e) 
				{
					$this->db->trans_complete();
					return 0;
				}			
			}
		}
		
		foreach ($this->answers as $ans)
		{
			if (!$ans->IsNew) continue;
			$data=$ans->GetData($this->id);
			try 
			{
				$this->db->insert('answerbasicinfo',$data);
				$ans->ID=$this->db->insert_id();
			} catch (Exception $e) 
			{
				$this->db->trans_complete();
				return 0;
			}		
		}
			
		foreach ($this->contents as $con)
		{
			if (!$con->IsNew) continue;
			$data=$con->GetData($this->id);
			try 
			{
				$this->db->insert('questioncontentinfo',$data);
				$con->id=$this->db->insert_id();
			} catch (Exception $e) 
			{
				$this->db->trans_complete();
				return 0;
			}
		}		
		
		foreach ($this->options as $opt)
		{
			if (!$opt->IsNew) continue;
			$data=$opt->GetData($this->id);
			try 
			{
				$this->db->insert('optionbasicinfo',$data);
				$opt->id=$this->db->insert_id();
			} catch (Exception $e) 
			{
				$this->db->trans_complete();
				return 0;
			}
		}
		
		if (!$IsModified)
		{
			if ($this->hasoptions==false) $this->AddQuestionsToPool(true);
			else 
				$this->AddQuestionsToPoolWithOptions(true);
		}

		$this->db->trans_complete();			
		return 1;
	}
	
	public function Modify()
	{
		//read to $contents,$answers,$options,$parameters and check
		$this->db->trans_start();
		
		//read&check
		
		//modify, be care of the orders of each operation
		//delete
		foreach ($this->contents as $con)
		{
			if ($con->IsDeleted)
			{
				$this->DelQuetionsFromPool($con);
				$pci=$con->ID;
				$sql="Delete from questioncontentinfo where ID=$pci";
				try {
					$this->db->query($sql);
				} catch (Exception $e) {
					$this->db->trans_complete();
					return 0;
				}
			}
		}
		
		foreach ($this->answers as $ans)
		{
			if ($ans->IsDeleted)
			{
				$this->DelQuetionsFromPool($ans);
				$ai=$ans->ID;
				$sql="Delete from answerbasicinfo where ID=$ai";
				try {
					$this->db->query($sql);
				} catch (Exception $e) {
					$this->db->trans_complete();
					return 0;
				}
			}
		}
		
		foreach($this->options as $opt)
		{
			if ($opt->IsDeleted)
			{
				$this->DelQuetionsFromPool($opt);
				$oi=$opt->ID;
				$sql="Delete from optionbasicinfo where ID=$oi";
				try {
					$this->db->query($sql);
				} catch (Exception $e) {
					$this->db->trans_complete();
					return 0;
				}
			}			
		}
		
		foreach($this->parameters as $param)
		{
			if ($param->IsModified || $param->IsDeleted)
			{
				foreach ($param->Values as $val)
				{
					if ($val->IsDeleted || $param->IsDeleted)
					{
						$vi=$val->ID;
						$sql="Delete from parametervalueinfo where ID=$vi";
						try {
							$this->db->query($sql);
						} catch (Exception $e) {
							$this->db->trans_complete();
							return 0;
						}
					}
				}
				
				if ($param->IsDeleted)
				{
					$pi=$param->ID;
					$sql="Delete from parameterbasicinfo where ID=$pi";
					try {
						$this->db->query($sql);
					} catch (Exception $e) {
						$this->db->trans_complete();
						return 0;
					}
				}
			}	
		}
	
		//add
		$this->Add(true);

		//modify,apply changes
		$this->ApplyDeleted();
		$this->Update();
				
		if ($this->hasoptions==false) $this->AddQuestionsToPool(false);
		else 
			$this->AddQuestionsToPoolWithOptions(false);
		
		$this->db->trans_complete();
		
		return 1;
	}
	
	private function ApplyDeleted()
	{
		foreach ($this->contents as $index=>$con)
		{
			if ($con->IsDeleted)
			{
				unset($this->contents[$index]);
			}
		}
		
		foreach ($this->answers as $index=>$ans)
		{
			if ($ans->IsDeleted)
			{
				unset($this->answers[$index]);
			}
		}
		
		foreach ($this->options as $index=>$opt)
		{
			if ($opt->IsDeleted)
			{
				unset($this->options[$index]);
			}
		}
		
		foreach ($this->parameters as $index=>$param)
		{
			if ($param->IsModified || $param->IsDeleted)
			{
				foreach ($param->Values as $index=>$val)
				{
					if ($val->IsDeleted || $param->IsDeleted)
					{
						unset($param->Values[$index]);
					}
				}
				
				if ($param->IsDeleted)
				{
					unset($this->parameters[$index]);
				}
			}	
		}		
	}
	
	private function Update()
	{
		foreach ($this->contents as $con)
		{
			if ($con->HasModified)
			{
				$this->DelQuetionsFromPool($con);
				$pci=$con->ID;
				$data=$con->GetData($this->id);
				$condition="ID=$pci";
				try 
				{
					$this->db->update_string('questioncontentinfo',$data,$condition);
				} catch (Exception $e) {
					return 0;
				}
			}
		}
		
		foreach ($this->answers as $ans)
		{
			if ($ans->HasModified)
			{
				$this->DelQuetionsFromPool($ans);
				$ai=$ans->ID;
				$data=$ans->GetData($this->id);
				$condition="ID=$ai";
				try {
					$this->db->update_string('answerbasicinfo',$data,$condition);
				} catch (Exception $e) {
					return 0;
				}
			}
		}
		
		foreach($this->options as $opt)
		{
			if ($opt->HasModified)
			{
				$this->DelQuetionsFromPool($opt);
				$oi=$opt->ID;
				$data=$opt->GetData($this->id);
				$condition="ID=$oi";
				try {
					$this->db->update_string('optionbasicinfo',$data,$condition);
				} catch (Exception $e) {
					return 0;
				}
			}			
		}
		
		foreach($this->parameters as $param)
		{
			if ($param->IsModified)
			{
				foreach ($param->Values as $val)
				{
					if ($val->HasModified)
					{
						$vi=$val->ID;
						$data=$val->GetData($param->ID);
						$condition="ID=$vi";
						try {
							$this->db->update_string('parametervalueinfo',$data,$condition);
						} catch (Exception $e) {
							return 0;
						}
					}
				}
				
				$pi=$param->ID;
				$data=$param->GetData($this->id);
				$condition=-"ID=$pi";
				try {
					$this->db->update_string('parameterbasicinfo',$data,$condition);
				} catch (Exception $e) {
					return 0;
				}
			}	
		}
		
		return 1;
	}
	
	public function ReadSearchParameters()
	{
		
	}
	
	public function Search()
	{
		
		//lead to so low efficiency, just for now
		//dificulty need to be modified
		$SP=$this->ReadSearchParameters();
		
		if ($SP->ID!=-1)
		{
			$id=$SP->ID;
			$sql="select ID,CategoryID,TaskID,CreationDate From questionbasicinfo where ID=$id";
		}
		else 
		{
			$where="";
			if ($SP->DifFrom!=-1 || $SP->DifTo!=-1)
			{
				$from=$SP->DifFrom;
				$to=$SP->DifTo;
				if ($from!=-1) $where=$where."(Diffculty>=$from and ";
				if ($to!=-1) $where=$where."(Diffculty<=$to and ";
			}
		
			if ($SP->DateFrom!='' || $SP->DateTo!='')
			{
				$from=$SP->DateFrom;
				$to=$SP->DateTo;
				if ($from!='') $where=$where."(a.CreationDate>=$from and ";
				if ($to!='') $where=$where."(a.CreationDate<=$to and ";
			}
		
			if ($SP->CategoryID!=-1)
			{
				$id=$SP->CategoryID;
				$where=$where."CategoryID=$id and ";
			}
			
			if ($SP->TaskID!=-1)
			{
				$id=$SP->TaskID;
				$where=$where."TaskID=$id and ";
			}
			$sql="select DISTINCT ID,CategoryID,TaskID,a.CreationDate From questionbasicinfo a,questionpool where ".$where."ID=QuestionID";
		}
		
		try {
		$query=$this->db->query($sql);
		} catch (Exception $e) {
			return 0;
		}
		
		foreach ($query->result() as $row)
		{
			$res["ID"]=$row->ID;
			$res["CreationDate"]=$row->CreationDate;
			
			$id=$row->ID;
			$sql="select Content from questioncontentinfo where QuestionID=$id LIMIT 1";
			$squery=$this->db->query($sql);
			$con=$squery->row();
			$res["Content"]=$con->Content;
			
			$id=$row->CategoryID;
			$sql="select Name from categorybasicinfo where ID=$id";
			$squery=$this->db->query($sql);
			$cate=$squery->row();
			$res["Category"]=$cate->Name;
			
			$id=$row->TaskID;
			$sql="select Name from taskbasicinfo where ID=$id";
			$squery=$this->db->query($sql);
			$task=$squery->row();
			$res["Task"]=$task->Name;
			//use the res structure to display
		}
				
		return 1;
	}
	
	private function AddQuestionsToPool($IsNew)
	{
		//choose content
		foreach ($this->content as $con)
		{
						
			//choose answer
			foreach ($this->answers as $ans)
			{
				$found=0;
				foreach ($ans->CounterParts as $cp)
				{
					if ($cp->ID==$con->ID)
					{
						$found=1;
						break;
					}
				}
				if ($found!=1) continue;

				//check new
				$new=false;
				if ($con->IsNew || $con->HasModified) $new=true;
				if ($ans->IsNew || $ans->HasModified) $new=true;
					
				if ($IsNew || (!$IsNew && $new))
				{
					$qi=$this->id;
					$qci=$con->ID;
					$ai=$ans->ID;
					$optb=0;
					$dif=$ans->Difficulty;
					$sql="insert into questionpool(QuestionID,QuestionContentID,AnswerID,CreationDate,Options,Difficulty) values($qi,$qci,$ai,NOW(),$optb,$dif)";
				}
			}	
		}
	}
	
	private function AddQuestionsToPoolWithOptions($IsNew)
	{
		//choose content
		foreach ($this->content as $con)
		{
			$optcnt=0;
			$optindex[$optcnt]=0;
			for ($i=0;$i<count($this->options);$i++) $optres[$i]=-1;
			//choose options
			do 
			{
				while ($optcnt<4 && $optcnt>=0)
				{
					if ($optindex[$optcnt]>count($this->options)-4+$optcnt)
					{
						$optcnt=$optcnt-1;
						$optindex[$optcnt]=$optindex[$optcnt]+1;
						continue;
					}
					$found=0;
					if ($optres[$optindex[$optcnt]]==-1)
					{
						foreach($this->options[$optindex[$optcnt]]->CounterParts as $cp)
						{
							if ($cp->ID==$con->ID)
							{	
								$found=1;
								break;
							}
						}				
						$optres[$optindex[$optcnt]]=$found;		
					}
					$found=$optres[$optindex[$optcnt]];

					if ($found==1)
					{
						$optcnt=$optcnt+1;
						$optindex[$optcnt]=$optindex[$optcnt-1]+1;	
					}
					if ($found==0) $optindex[$optcnt]=$optindex[$optcnt]+1;
					if ($found!=1 || $found!=0) throw new Exception("Error In Generate QuestionPool!!!");
				}
				if ($optcnt<0) break;
			
				//choose answer

				foreach ($this->answers as $ans)
				{
					$found=0;
					foreach ($ans->CounterParts as $cp)
					{
						if ($cp->ID==$con->ID)
						{
							$found=1;
							break;
						}
					}
					if ($found!=1) continue;
					
					//check new
					$new=false;
					if ($con->IsNew || $con->HasModified) $new=true;
					for ($i=0;$i<4;$i++)
					{
						if ($this->options[$optindex[$optcnt]]->IsNew || $this->options[$optindex[$optcnt]]->HasModified)
						{
							$new=TRUE;
							break;
						}
					}
					if ($ans->IsNew || $ans->HasModified) $new=true;
					
					if ($IsNew || (!$IsNew && $new))
					{
						$qi=$this->id;
						$qci=$con->ID;
						$ai=$ans->ID;
						$optb=0;
						for ($i=0;$i<4;$i++)
						{
							$optb=$optb | 1<<$optindex[$i];
						}
						$dif=$ans->Difficulty;
						$sql="insert into questionpool(QuestionID,QuestionContentID,AnswerID,CreationDate,Options,Difficulty) values($qi,$qci,$ai,NOW(),$optb,$dif)";
						$this->db->query($sql);
					}
				}
				
				$optcnt=$optcnt-1;
				$optindex[$optcnt]=$optindex[$optcnt]+1;
			}while(true);
			
			
		}
	}
	
	private function DelQuetionsFromPool($obj)
	{
		if ($obj instanceof QuestionContent)
		{
			$qi=$this->id;
			$qci=$obj->ID;
			try {
				$sql="delete from questionpool where QuestionID=$qi and QuestionContentID=$qci";
				$this->db->query($sql);
			} catch (Exception $e) {
				return 0;
			}
			return 1;
		}
		
		if ($obj instanceof Answer)
		{
			$qi=$this->id;
			$ai=$obj->ID;
			try {
				$sql="delete from questionpool where QuestionID=$qi and AnswerID=$ai";
				$this->db->query($sql);
			} catch (Exception $e) {
				return 0;
			}
			return 1;			
		}
		
		if ($obj instanceof Option)
		{
			$qi=$this->id;
			if ($obj->Index==-1) throw new Exception("Invalid Option Index");
			$optb=1<<$obj->Index;
			try {
				$sql="delete from questionpool where QuestionID=$qi and Options&$optb";
				$this->db->query($sql);
			} catch (Exception $e) {
				return 0;
			}
			return 1;
		}
	}
}

Class QuestionContent
{
	public $ID;
	public $Content;
	public $IsNew;
	public $IsDeleted;
	public $HasModified;
	public $Parameters;
	public $Answer;
	
	public function __construct()
	{
		$this->IsNew=false;
		$this->IsDeleted=false;
		$this->HasModified=false;
	}
	
	public function GetParameters()
	{
		return Parameter::GenerateParameters($this->Parameters);
	}
	
	public function FillParameters($str,&$params)
	{
		$this->Parameters=Parameter::FillParameters($str, $params);	
	}
	
	public function FillAnswer($aid,&$answers)
	{
		$this->Answer=&$answers[$aid];
		$answers[$aid]->CounterParts[$this->ID]=&$this;
	}
	
	public function GetData($id)
	{
		$data=array(
		'Content'=>$this->Content,
		'QuestionID'=>$id,
		'AnswerID'=>$this->Answer->ID,
		'Parameters'=>$this->GetParameters());
		
		return $data;
	}
}

Class Answer
{
	public $ID;
	public $Content;
	public $IsDeleted;
	public $CounterParts;
	public $IsNew;
	public $HasModified;
	public $Parameters;
	public $Difficulty;
	
	public function __construct()
	{
		$this->IsNew=false;
		$this->IsDeleted=false;
		$this->HasModified=false;
	}

	public function GetParameters()
	{
		return Parameter::GenerateParameters($this->Parameters);
	}
	
	public function FillParameters($str,&$params)
	{
		$this->Parameters=Parameter::FillParameters($str, $params);
	}
	
	public function GetData($id)
	{
		$data=array(
			'Content'=>$this->Content,
			'QuestionID'=>$id,
			'Parameters'=>$this->GetParameters());
		
		return $data;
	}
}

Class Option
{
	public $ID;
	public $Content;
	public $CounterParts;
	public $IsNew;
	public $IsDeleted;
	public $HasModified;
	public $Parameters;
	public $Index;
	
	public function __construct()
	{
		$this->IsNew=false;
		$this->IsDeleted=false;
		$this->HasModified=false;
		$this->Index=-1;
	}

	public function GetParameters()
	{
		return Parameter::GenerateParameters($this->Parameters);
	}
	
	public function FillParameters($str,&$params)
	{
		$this->Parameters=Parameter::FillParameters($str, $params);
	}
	
	public function GetRelatives()
	{
		$str="";
		foreach ($this->CounterParts as $part)
		{
			$str=$str.$part->ID.";";
		}
		
		return $str;
	}
	
	public function FillRelatives($str,&$contents)
	{
		if ($strlen($str)<=1) throw new Exception("Invalid Data!");
		
		$res=explode(";",$str);
		foreach ($res as $sub)
		{
			$id=intval($sub);
			$this->CounterParts[$id]=&$contents[$id];
		}
	}
	
	public function GetData($id)
	{
		$data=array(
			'Content'=>$this->Content,
			'QuestionID'=>$id,
			'RelativeQuestion'=>$this->GetRelatives(),
			'Parameters'=>$this->GetParameters());
		
		return $data;
	}
}

Class Parameter
{
	public $ID;
	public $Name;
	public $Values;
	public $IsNew;
	public $IsDeleted;
	public $HasModified;
	
	public static function GenerateParameters(&$params)
	{
		$str="";
		foreach ($params as $param)
		{
			$str=$str.$param->ID.";";
		}
		
		return $str;
	}
	
	public static function FillParameters($str,&$params)
	{
		if ($strlen($str)<=1) return null;
		
		$res=explode(";",$str);
		foreach ($res as $sub)
		{
			$id=intval($sub);
			$pr[$id]=&$params[$id];
		}
		
		return pr;
	}
	
	public function __construct()
	{
		$this->IsNew=false;
		$this->IsDeleted=false;
		$this->HasModified=false;
	}
	
	public function GetData($id)
	{
		$data=array(
				'Name'=>$this->Name,
				'QuestionID'=>$id);
		return $data;
	}
}

Class ParameterValues
{
	public $ID;
	public $Content;
	public $Type;
	public $From;
	public $To;
	public $IsNew;
	public $IsDeleted;
	public $HasModified;
	
	public function __construct()
	{
		$this->IsNew=false;
		$this->IsDeleted=false;
		$this->HasModified=false;
	}
	
	public function GetData($id)
	{
		$data=array(
				'Value'=>$this->Content,
				'Type'=>$this->Type,
				'From'=>$this->From,
				'To'=>$this->To,
				'ParameterID'=>$id);
		return $data;
	}
}

class SearchParameters
{
	public $ID;
	public $DateFrom;
	public $DateTo;
	public $DifFrom;
	public $DifTo;
	public $CategoryID;
	public $TaskID;
	
	public function __construct()
	{
		$this->ID=-1;
		$this->DateFrom='';
		$this->DateTo='';
		$this->DifFrom=-1;
		$this->DifTo=-1;
		$this->CategoryID=-1;
		$this->TaskID=-1;
	}
}

?>